package projeto.lp2.grupo6.logica;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * Aluno: 
 *      Guilherme Monteiro 
 * 	Italo Guedes 
 * 	Tiago Leite
 * 
 * @author guilhermemg
 * @author tiagoln
 * @author italogas
 * 
 * Classe utilizada para gerar recomendacoes de estabelecimentos mais populares.
 */
public class RecomendacaoPorPopularidade extends Recomendacao {

    private Map<Estabelecimento, Integer> mapaDePopularidade;

    /**
     * Contrutor da classe
     * 
     * @param listaUsuarios - Lista com os usuarios do sistema
     * @throws Exception 
     */
    public RecomendacaoPorPopularidade(List<Usuario> listaUsuarios) throws Exception {
        super(listaUsuarios);
        mapaDePopularidade = new LinkedHashMap<Estabelecimento, Integer>();
    }

    /**
     * * Este metodo e utilizado para retornar uma lista dos estabelecimentos mais populares.
     * Ele cria um mapa de estabelecimentos como chaves, e o valor e o somatorio das notas.
     * 
     * @param quantidadeItens - Quantidade de itens a serem inseridos na lista
     * @param filtroTipo - Filtro de tipos de estabelecimento
     * @param palavraChave - Filtro de palavra chave
     * @return Uma lista com os estabelecimentos mais populares
     * @throws Exception 
     */
    @Override
    public List<Estabelecimento> getRecomendacao(int quantidadeItens, List<TipoDeAlmocoServido> filtroTipo, String palavraChave) throws Exception {
        if (quantidadeItens < 0) {
            throw new Exception("Erro em quantidade de itens negativa");
        }
        if (quantidadeItens == 0) {
            throw new Exception("Erro em quantidade de itens nula");
        }
        if (filtroTipo == null) {
            throw new Exception("Erro em lista de tipos de almoco servidos nula");
        }
        if (filtroTipo.isEmpty()) {
            throw new Exception("Erro em lista de tipos de almoco servidos vazia");
        }
        if (palavraChave == null) {
            throw new Exception("Erro em palavra-chave nula");
        }
        if (palavraChave.isEmpty()) {
            throw new Exception("Erro em palavra-chave vazia");
        }

        Iterator<Usuario> iteradorUsuarios = getListaUsuarios().iterator();
        Iterator<Opiniao> iteradorOpinioes;
        while (iteradorUsuarios.hasNext()) {
            Usuario usuario = iteradorUsuarios.next();
            iteradorOpinioes = usuario.getListaDeOpinioes().iterator();
            while (iteradorOpinioes.hasNext()) {
                Opiniao opiniao = iteradorOpinioes.next();
                if (!mapaDePopularidade.keySet().contains(opiniao.getEstabelecimento())) {
                    mapaDePopularidade.put(opiniao.getEstabelecimento(), opiniao.getNota());
                } else {
                    mapaDePopularidade.put(opiniao.getEstabelecimento(),
                            (mapaDePopularidade.get(opiniao.getEstabelecimento()) + opiniao.getNota()));
                }
            }

        }
        mapaDePopularidade = ordenaMapaPopularidade(mapaDePopularidade);
        List<Estabelecimento> listaResultado = new ArrayList<Estabelecimento>();
        for (Estabelecimento estabelecimento : mapaDePopularidade.keySet()) {
            if (listaResultado.size() < quantidadeItens
                    && filtroTipo.contains(estabelecimento.getTipoDeAlmoco())
                    && !estabelecimento.getNome().toLowerCase().contains(palavraChave.toLowerCase())) {
                listaResultado.add(estabelecimento);
            } else if (listaResultado.size() >= quantidadeItens) {
                break;
            }
        }
        return listaResultado;
    }

    /**
     * Este metodo e utilizado para ordenar o mapa de estabelecimentos pelo maior somatorio de notas.
     * 
     * @param mapaEstabelecimentos - O mapa a ser ordenado.
     * @return Um mapa ordenado por maior valor.
     */
    private static Map<Estabelecimento, Integer> ordenaMapaPopularidade(Map<Estabelecimento, Integer> mapaEstabelecimentos) {
        List<Map.Entry<Estabelecimento, Integer>> listaDeEntradas = new LinkedList<Map.Entry<Estabelecimento, Integer>>(mapaEstabelecimentos.entrySet());
        Collections.sort(listaDeEntradas, new Comparator<Map.Entry<Estabelecimento, Integer>>() {

            @Override
            public int compare(Entry<Estabelecimento, Integer> o1,
                    Entry<Estabelecimento, Integer> o2) {
                return (o1.getValue()).compareTo(o2.getValue());
            }
        });
        Collections.reverse(listaDeEntradas);

        Map<Estabelecimento, Integer> mapaOrdenadoPorNota = new LinkedHashMap<Estabelecimento, Integer>();
        for (Iterator<Map.Entry<Estabelecimento, Integer>> it = listaDeEntradas.iterator(); it.hasNext();) {
            Map.Entry<Estabelecimento, Integer> entradaNoMapa = it.next();
            mapaOrdenadoPorNota.put(entradaNoMapa.getKey(), entradaNoMapa.getValue());
        }
        return mapaOrdenadoPorNota;
    }
}
